KinematicBody2D is a node of choice to code fine-grained movement.
Characters, cars with steering, pushable blocks in Zelda games, and generally anything that needs to collide with walls or other game entities are great candidates for a KinematicBody2D.
If you want more realistic Newtonian physics and to let the physics engine calculate movement for you, you’ll want to use RigidBody2D instead.
You typically use a KinematicBody2D for tight, pixel-perfect movement and anything that moves and collides in your game where you want complete control over the motion.
In this guide, you will learn:
KinematicBody2D’s collisions. The node’s key features include moving characters manually, getting information about collisions, and more.KinematicBody2D in your games to create side-scrolling and top-down character controllers.Contents:
Like other physics bodies in Godot, a KinematicBody2D requires one or more CollisionShape2D or a CollisionPolygon2D node as a child.
The collision nodes define the shape the physics engine will use to check for collisions with the environment.
You can always use more than one shape to approximate the shape of your character or entity.
KinematicBody2D comes packed with functions to create character controllers with ease:
move_and_slide() and move_and_slide_with_snap(), handle a lot under the hood for you and help you start coding movement quickly.is_on_floor(), is_on_wall(), and is_on_ceiling() tell you if your entity is on the floor, against a wall, or on the ceiling. These are great for side-scrolling games.move_and_collide() and test_motion() functions.The most valuable function of a KinematicBody2D is move_and_slide().
It takes a velocity and moves your entity accordingly in one frame.
Time delta is applied internally, meaning bodies will move at the same speed regardless of the framerate the user is experiencing.
If the body hits an obstacle, the function slides the body against it.
Technically, it takes the leftover motion at the collision point and projects the remaining velocity alongside it.
If any motion remains after colliding, the node repeats the operation up to 5 times, allowing your entity to slide smoothly along curved obstacles.
move_and_slide() also takes other moving bodies into account. For example, if a character is on a moving platform, both will move together.
The move_and_slide_with_snap() function is a more powerful alternative for side-scrollers with slopes. When walking on a slope, you can use it to snap your character to the slope.
You do that using the snap argument, a vector representing a distance to the floor below which your character will snap to it.
Both the move_and_slide() and move_and_slide_with_snap() functions also allow you to define the “up” vector.
This argument helps the engine determine what direction is “up” and, therefore, where the floor, ceilings, and walls are.
Note we put “up” in quotes because you can use that to reverse gravity in your game or allow characters to move around planets, and more.
You’ll want to change the “up” vector in a game where you change the direction of gravity, turning the walls and ceiling into the floor.
It’s also useful for 360-degree platforming to create loops and curves in games like Sonic the Hedgehog.
The default value for “up” is Vector2.ZERO. In this case, the KinematicBody2D treats all surfaces as a wall, ideal for top-down games.
What if you want a projectile to bounce against surfaces? With move_and_slide(), when it hits a wall, the projectile will slide alongside it.
Or perhaps you want a feel you can’t achieve easily with move_and_slide(), like an old-school pixel-perfect character movement.
That’s what move_and_collide() is for. This function moves the body with a motion vector. That’s unlike move_and_slide(), with takes a velocity.
The move_and_collide() function moves the character given that input motion and collides along the way. If a collision occurs, the function returns a object, which gives you information about what happened. You then use that information to code your intended movement.
Godot uses move_and_collide() as a base internally for move_and_slide() and move_and_slide_with_snap().
When collisions occur, you can get detailed information about them. Whether you use move_and_slide() or move_and_collide(), you can get a instance to know where, when, and between which physics shapes the collision occurred.
If you use move_and_collide() and a collision occurs, the function returns the instance. We typically store it and check for it like so:
var collision := move_and_collide(motion) if collision: # ...
With move_and_slide(), as we saw above, the engine may move your body multiple times to smooth out its motion. The function itself returns a new velocity taking into account any obstacle that affected your body.
If you want collision information, you need to request it. To do so, you’ll use two functions: get_slide_count() and get_slide_collision().
The get_slide_count() function tells you the number of collisions that occurred after calling move_and_slide(). You use it to ensure a given collision exists when calling get_slide_collision(), like so:
move_and_slide(velocity) for index in get_slide_count(): var collision := get_slide_collision(index) # ...
KinematicBody2D provides three functions to know if your entity is touching a surface at any point in time:
is_on_floor().is_on_wall().is_on_ceiling().They return true if the body is against the corresponding surface.
These functions only work after moving your KinematicBody2D and having it collide with that surface. Otherwise, the physics engine assumes your entity is in the air.
As a result, you always want to apply some gravity to your characters and monsters in a side-scrolling game. You’ll see we always do that in the demos below.
You can use those functions to check if the player can jump without a dedicated can_jump variable.
Many of these demos build upon the same base code, unlike other node essentials guides.
We created a base script with shared constants, properties, and functions. Some demos extend it and use the base functions or build upon them.
# Example from PlayerMovingPlatforms.gd extends BasePlayerSideScroll func _physics_process(delta: float) -> void: update_animation() update_look_direction() apply_base_movement(delta)
We’ll start by covering the base class and how we coded running and jumping before tackling the other ways to use KinematicBody2D.
Note that we wrote small functions to help you read the code and make it reusable.
We don’t necessarily recommend splitting your code this way in your games; we chose this style for the needs of the node essentials demos and this guide.
The KinematicBody2D node requires coding all of your movement logic by yourself. Here, you’ll get a detailed look at a base class for side-scrolling movement.
This code is more fleshed out as we took the time to name all the values we use with constants and create functions to make the code readable and reusable in other demos.
In this example, the character can:
The scene relies on one KinematicBody2D node and a CollisionShape2D. The PlayerSideSkin is an animated cut-out character.
You can find the PlayerSideSkin in the Node Essentials Godot project, inside the res://common/ directory.
To implement all the mechanics above, we need to tell Godot some values to know how to move our body. So we store them as constants.
## A direction vector perpendicular to the floor. We pass it to the ## move_and_slide_with_snap() function so Godot knows if the body is on the ## floor, touching a wall, or against the ceiling. const UP_DIRECTION := Vector2.UP # We use these constant to calculate a snap vector when we want the character to # snap to the floor. # # We use them below to initialize the `_snap_vector` variable. const SNAP_DIRECTION := Vector2.DOWN const SNAP_VECTOR_LENGTH := 32.0 ## This is the maximum number of times Godot will try to move and slide the body ## against a wall or a slope to move it smoothly. const MAX_SLIDES := 4 ## The maximum angle a slope can have before the character starts to slide down, ## in radians. ## ## We use the deg2rad() function and to calculate a maximum slope of 46°, ## allowing the player to walk slopes with a 45° angle. const MAX_SLOPE_ANGLE := deg2rad(46)
Then come the properties, some of which we export to make them editable in the Inspector.
## The character's horizontal movement speed in pixels per second. ## ## When pressing the left and right arrow keys, the character instantly goes at ## this speed, as in games like Megaman. export var speed := 600.0 ## When the character jumps, we set their upward velocity to this value. export var jump_strength := 1400.0 ## This is the gravity in pixels per second squared, which we will apply every frame. export var gravity := 4500.0 ## The following 2 properties are arguments of the move_and_slide() function. We ## exported variables to toggle them on and off in different demos. export var do_stop_on_slope := false export var has_infinite_inertia := true ## The following pseudo-private variables keep track of the characters velocity, ## the snap vector, and the horizontal input direction, which we change regularly ## in the code. var _velocity := Vector2.ZERO var _snap_vector := SNAP_DIRECTION * SNAP_VECTOR_LENGTH var _horizontal_direction := 0.0 ## The following properties allow us to animate the character we designed for ## this demo and to mirror it horizontally. onready var _pivot: Node2D = $PlayerSideSkin onready var _anim_player: AnimationPlayer = $PlayerSideSkin/AnimationPlayer onready var _start_scale := _pivot.scale
We then define a function to calculate the character’s motion and move it, which has three steps:
move_and_slide_with_snap() function.Here is the base input and velocity calculation.
## Gets the player's input, updates the velocity, and moves the character. func apply_base_movement(delta: float) -> void: # Input.get_action_strength() Returns a value between zero and one-based on # the players input. # # For buttons, the value will either be zero or one, but this function # supports analog input for joysticks and trigger buttons, giving you a # whole range of possible values. _horizontal_direction = ( Input.get_action_strength("move_right") - Input.get_action_strength("move_left") ) # The character moves horizontally without acceleration, but we constantly # apply the gravity to trigger collisions with the floor. _velocity.x = _horizontal_direction * speed _velocity.y += gravity * delta
Next, we look at the character’s current state to modify the velocity.
if is_jumping(): _velocity.y = -jump_strength _snap_vector = Vector2.ZERO elif is_jump_cancelled(): _velocity.y = 0.0 elif is_landing(): _snap_vector = SNAP_DIRECTION * SNAP_VECTOR_LENGTH
You can think of the snap vector like a ray cast from the KinematicBody2D’s origin.
If it touches the floor, the move_and_slide_with_snap() function will snap the character to the floor.
This function is handy when walking on a slope as it prevents the character from hopping down each step.
As you can see, we use some functions we define ourselves to check if the character is jumping, landing, etc.
We wrote them that way to help you read the code and to reuse them across demos.
## Returns `true` if the player is initiating a jump. func is_jumping() -> bool: return Input.is_action_just_pressed("jump") and is_on_floor() ## Returns `true` if the player released the jump input action while jumping. func is_jump_cancelled() -> bool: return Input.is_action_just_released("jump") and _velocity.y < 0.0 ## Returns `true` if the character is landing on the floor this frame. func is_landing() -> bool: return _snap_vector == Vector2.ZERO and is_on_floor() ## Returns `true` if the character is falling. func is_falling() -> bool: return _velocity.y > 0.0 and not is_on_floor() ## Returns `true` if the character is on the floor, running. func is_running() -> bool: return is_on_floor() and not is_zero_approx(_horizontal_direction) and not is_on_wall()
Back to the apply_base_movement() function, we call move_and_slide_with_snap() to move the character.
_velocity = move_and_slide_with_snap(
_velocity,
_snap_vector,
UP_DIRECTION,
do_stop_on_slope,
MAX_SLIDES,
MAX_SLOPE_ANGLE,
has_infinite_inertia
)
Next are the functions to play an animation depending on the character’s state. For example, when jumping, we play the jump animation.
We can utilize our boolean functions for these. To play the right animation, we use a series of conditional statements.
## Updates the playing animation based on the character's state. ## To override in child classes to add support for more animations. func update_animation() -> void: if is_jumping(): _anim_player.play("jump") elif is_running(): _anim_player.play("run") elif is_falling(): _anim_player.play("fall") else: _anim_player.play("idle")
Finally, we want the character to face the way they are moving. For this, we flip just the characters’ sprites horizontally. This avoids flipping anything physics-related like the collision shapes.
## Flips the character horizontally to match the player's input direction. func update_look_direction() -> void: if not is_zero_approx(_horizontal_direction): _pivot.scale.x = sign(_horizontal_direction) * _start_scale.x
That in itself does not move the character. To move it, we have to call these functions from _physics_process(), like so.
extends BasePlayerSideScroll func _ready() -> void: ## If we call move_and_slide() and request to stop on slopes, the character ## will also stop on moving platforms instead of moving with them. ## ## You can distinguish between slopes and moving platforms by getting ## collision information and checking the collider's velocity. do_stop_on_slope = false func _physics_process(delta: float) -> void: update_animation() update_look_direction() apply_base_movement(delta)
Notice how we update the animations every frame, calling AnimationPlayer.play() repeatedly.
Thankfully, when you try to play the same animation multiple times in a row, and it is already playing, the AnimationPlayer node is smart enough to keep playing the animation without restarting it.
All the above is a base you can use to create side-scrolling characters and the base we use in the following examples.
In just a few lines of code, you can extend a base character controller with a stomping mechanic as in Mario games.
To stomp an enemy, we need to check if the player lands on their head. If we use rectangular collision shapes, we can directly use the KinematicBody2D.is_on_floor function to do so.
We loop through all the collisions that may have occurred after calling move_and_slide() and ensure that we collided with an enemy.
## Checks if we fell on an enemy and if so, kills the enemy and makes the ## character jump. func stomp() -> void: # If we fell on top of an enemy, KinematicBody2D considers that we are on # the floor. We only run the stomp code if is_on_floor() returns true and # we're landing this frame. if not (is_landing() and is_on_floor()): return for index in get_slide_count(): # We loop over all the collisions this frame and if one of the things we # collided with is an enemy, we destroy it and jump. var collision := get_slide_collision(index) # To detect enemies, we use a node group here, but you could also use # the is keyword and check for a specific type. # # Or you could use duck typing and check that the entity has a "die" # function for example. if collision.collider.is_in_group("enemy"): collision.collider.queue_free() _velocity.y -= stomp_bump_strength
Here, we extend our BasePlayerSideScroll class and call the parent class’s functions in _physics_process.
Afterward, we call the stomp function.
## Side-scrolling playable character with the ability to jump, run, cancel jump, ## and stomp enemies. extends BasePlayerSideScroll # Similarly to jump_strength, we apply this to the character's vertical velocity # and make it do hop after stomping an enemy. export var stomp_bump_strength := 1000.0 # The character uses the same parent class as the basic side-scroll movement # demo, but we add a stomp function in case we fall on an enemy. func _physics_process(delta: float) -> void: update_animation() update_look_direction() apply_base_movement(delta) stomp()
If you use collision shapes other than rectangles, is_on_floor() should work as long as the angle between your character’s shape and the enemy’s shape is less than the maximum slope angle at the collision point.
You can use KinematicBody2D to create moving platforms. You can move them directly with move and slide, like any moving entity in your world. You can also use the animation player to design moving platforms.
In our demo, each moving platform is a KinematicBody2D with an AnimationPlayer that animates its position. We use a looping animation for that.
In that case, you must use the sync to physics option in the Inspector. It allows the physics engine to use changes in the node’s position when calculating collisions in the world.
But then you have the seemingly difficult task of making this moving platform able to move whatever is sitting on top of it. Thankfully, this is a feature that comes for free with the move_and_slide() family functions.
You only need to change the 5th argument, stop_on_slope, to false. Stopping on slopes makes the entity stay in place when on the floor or a moving platform.
In our code, we use a property named do_stop_on_slope that we pass to move_and_slide_with_snap(). So all we have to do is to toggle the property in the Inspector.
# ANCHOR: base_movement extends BasePlayerSideScroll func _ready() -> void: ## If we call move_and_slide() and request to stop on slopes, the character ## will also stop on moving platforms instead of moving with them. ## ## You can distinguish between slopes and moving platforms by getting ## collision information and checking the collider's velocity. do_stop_on_slope = false # ANCHOR: processing func _physics_process(delta: float) -> void: update_animation() update_look_direction() apply_base_movement(delta) # END: processing # END: base_movement func apply_base_movement(delta: float) -> void: _horizontal_direction = ( Input.get_action_strength("move_right") - Input.get_action_strength("move_left") ) _velocity.x = _horizontal_direction * speed # ANCHOR: lower_velocity # Prevent the character from moving up slopes past the `MAX_SLOPE_ANGLE`. # # This crude code leads to cases where the character alternates between idle # and run animations quickly. # # To address it, you need a more robust way of staying in the idle state # when against a rotating platform. For instance, using a short RayCast2D at # their feet. if is_on_wall(): _velocity.x = sign(_horizontal_direction) * 1.0 # END: lower_velocity _velocity.y += gravity * delta if is_jumping(): _velocity.y = -jump_strength _snap_vector = Vector2.ZERO elif is_jump_cancelled(): _velocity.y = 0.0 elif is_landing(): _snap_vector = SNAP_DIRECTION * SNAP_VECTOR_LENGTH _velocity = move_and_slide_with_snap( _velocity, _snap_vector, UP_DIRECTION, do_stop_on_slope, MAX_SLIDES, MAX_SLOPE_ANGLE, has_infinite_inertia )
Building upon moving platforms, we can use KinematicBody2D to move on rotating surfaces.
Like with moving platforms, you can use the animation player to animate their rotation.
To do so, you want to set the animation player to update on the physics callback and enable the platforms’ Sync To Physics property to interact with the character correctly.
In our example, we created a bridge that can go up and prevent the player from crossing a gap.
You have to be careful with the way you handle horizontal velocity because if you are climbing the bridge as it rotates up, your character will climb the slope even if it becomes too steep.
The reason is that when you move to the right, with move_and slide, Godot tries to transfer the velocity sliding against the surface upwards.
As long as the surface is not perpendicular, a bit of velocity will move up the slope, even with our maximum slope angle.
In fact, the max_slope_angle argument mainly distinguishes between your KinematicBody2D being on the floor and the wall. It’s a threshold to distinguish the two states.
So, to fall down the bridge when it gets too steep, we can check if the character is on a wall and limit their ability to move into the wall.
# Prevent the character from moving up slopes past the `MAX_SLOPE_ANGLE`. # # This crude code leads to cases where the character alternates between idle # and run animations quickly. # # To address it, you need a more robust way of staying in the idle state # when against a rotating platform. For instance, using a short RayCast2D at # their feet. if is_on_wall(): _velocity.x = sign(_horizontal_direction) * 1.0
This example is about having a push mechanic in your game where the character stops against a rock and pushes it.
Here, we focus on pushing a RigidBody2D node, a rock controlled by the physics engine that will keep rolling after you push it.
To learn to push a KinematicBody2D, see Pushing objects in a top-down game.
Kinematic bodies can interact with other bodies. By default, if you call move_and_slide() with the infinite_inertia property set to true, your kinematic body will move any rigid body out of its way.
What if you want to stop when touching a RigidBody2D and control the pushing force instead?
To do so, you want to turn off infinite_inertia first. Starting from our base script, we can change the has_infinite_inertia property.
## The "force" applied to the rigid body we are pushing. export var push_strength := 6000.0 # We create a property to keep track of the pushing state as it relies on looping over the # collisions. It would not be convenient to make as a reusable function. var _is_pushing := false func _ready() -> void: # Infinite inertia allows KinematicBody2D to push RigidBody2D nodes around # without getting slowed down. This is perfect for debris and other cosmetic # objects to push around, but would break our pushing mechanic. has_infinite_inertia = false
To push the body, we check if we are colliding with a rigid body, and if so, we apply an impulse to it in our direction of motion.
## Attempts to push a colliding rigid body. func push(delta: float) -> void: # We have to always reset the property to only set it to true when the # character is pushing something. _is_pushing = false for index in get_slide_count(): var collision := get_slide_collision(index) var collider := collision.collider # We loop over all the collisions that occured this frame and look for a # RigidBody2D. if collider is RigidBody2D: # We ensure that one not above the body using a vector.product. We # use it to compare the angle between the floor normal and the # collision normal. # # A value lower than 0.9 means that the 2 vectors are at an angle # lower than 90°. _is_pushing = collision.normal.dot(UP_DIRECTION) < 0.9 _velocity.x = _horizontal_direction * push_strength # We apply an impulse to push the body we are colliding with. You # can learn more about RigidBody2D and its functions in the # RigidBody2D guide. collider.apply_central_impulse(_velocity.normalized() * push_strength * delta) break
We call our push() function in _physics_process().
func _physics_process(delta: float) -> void: apply_base_movement(delta) push(delta) update_animation() update_look_direction()
As we apply an impulse to the rocks, they will keep rolling for a while even when we stop pushing.
We can then use the _is_pushing property to update our current animation.
## We override the parent class's update_animation() to add support for the push animation. func update_animation() -> void: if _is_pushing: _anim_player.play("push") # ...
Wall jumping and coyote time are great additions to a sidescroller, with wall jumping letting skilled players reach new locales and coyote time giving some much-needed forgiveness when falling off platforms or the wall in question.
Here, we have a character that can stick to walls, slide down, fall off, and wall jump right after using a popular mechanic called coyote time.
Coyote time consists of starting a timer right after a character starts falling and still allowing them to jump. It’s essential for modern platform games.
The code is heavier than most examples in node essentials and requires writing custom movement logic to take walls into account.
Note that any property we don’t show below here comes from the parent BasePlayerSideScroll class we showed before: Basic side-scrolling movement.
We introduce a few new properties for the wall jump and the coyote time.
export var wall_jump_strength := 2400.0 export var wall_friction_multiplier := 0.5 ## When this timer is active, the player can jump even if not on the floor or a wall. onready var _coyote_timer: Timer = $CoyoteTimer
In the processing loop, we start by calculating the base velocity as usual.
func _physics_process(delta: float) -> void: _horizontal_direction = ( Input.get_action_strength("move_right") - Input.get_action_strength("move_left") ) _velocity.x = _horizontal_direction * speed _velocity.y += gravity * delta
Next is the wall sliding logic.
func _physics_process(delta: float) -> void: # ... # When the character is on a wall, we push them in the wall's direction to # make them stick to it. # # We also use the variable to jump away from the wall. var wall_direction := 0.0 # The character slides as long as you're on the wall and keep the direction # key towards the wall. if is_wall_sliding(): wall_direction = sign(_horizontal_direction) # We apply some friction to slow down the character's fall. _velocity *= wall_friction_multiplier # And we push the character towards the wall to make them stick to it. _velocity.x += wall_direction * gravity * delta _coyote_timer.stop()
We’ll look at the is_wall_sliding() function and other boolean functions in a moment.
We first continue with the jumping and landing logic. It is more complex than in the parent class due to the introduction of wall jump and coyote time.
func _physics_process(delta: float) -> void: # ... # When the character starts falling, we start the coyote timer, allowing the # player to jump once mid-air. # # When jumping or landing, we stop the timer. if is_jumping() or is_wall_jumping(): _velocity.y = -jump_strength _snap_vector = Vector2.ZERO # When we jump, whether we used our coyote time or not, we don't want to # allow for another jump so we stop the timer. _coyote_timer.stop() elif is_jump_cancelled(): _velocity.y = 0.0 elif is_landing(): _snap_vector = SNAP_DIRECTION * SNAP_VECTOR_LENGTH # When landing, we reset the coyote time properties to re-enable them # next time we fall. _coyote_timer.stop() # To allow jumping at the start of a fall, we start our timer. As long as it # is running and the player has not jumped yet, we allow them to jump once. elif is_just_falling() and is_on_wall() or is_on_floor(): _coyote_timer.start() if is_wall_jumping(): _velocity.x = -wall_jump_strength * wall_direction
Finally, we call move_and_slide_with_snap() to move the character — nothing new here.
func _physics_process(delta: float) -> void: # ... _velocity.y = move_and_slide_with_snap( _velocity, _snap_vector, UP_DIRECTION, do_stop_on_slope, MAX_SLIDES, MAX_SLOPE_ANGLE ).y
Here are the functions that tell us if the character has just fallen, is wall jumping, or is sliding on a wall.
## Returns `true` if the character starts falling. func is_just_falling() -> bool: return not _anim_player.current_animation == "fall" and is_falling() ## Returns `true` if the character is initiating a wall-jump. func is_wall_jumping() -> bool: return is_on_wall() and Input.is_action_just_pressed("jump") ## Returns `true` if the character is sliding against a wall. func is_wall_sliding() -> bool: return is_on_wall() and not is_zero_approx(_horizontal_direction)
Now, this is not a complete and polished wall jump system. Creating a very refined platform controller takes more code than that.
In particular, to wall jump away from a wall, you want to allow the player to stick to the wall for a few frames after they pressed the key to move away from it.
To do this efficiently, you need a code structure to separate the character’s different states, like a finite state machine.
To learn to create a finite state machine, see How do I code a finite state machine?.
There are many reasons you may want a body to pass through another, dropping down through platforms, dashing through enemies, teleporting, becoming invulnerable for some time, and much more.
You’ll often want to let the player fall down a platform in platform games by pressing a key. To do so, you need to toggle off collision detection temporarily for that platform.
In our demo, we use a dedicated TileMap node to separate pass-through platforms from other floors and walls. We set it to the physics layer 5, while the rest of the environment is on layer 4.
## Index of the layer we want to ignore when dashing or falling through the floor. const PASS_THROUGH_LAYER := 4 # We store a reference to our CollisionShape2D to do collision checks manually. # See the is_inside_wall() function. onready var _collision_shape: CollisionShape2D = $CollisionShape2D
Here is the gist of the pass-through logic:
func _physics_process(delta: float) -> void: # ... # When the player jumps down, we turn off collisions for the platform. if Input.is_action_just_pressed("jump") and Input.is_action_pressed("move_down"): set_pass_through_walls_and_floor(true) # And when we passed through the platform, we reactivate collisions. elif is_passing_through_walls() and not is_inside_wall(): # We use a collision check to ensure the character won't get stuck # inside a platform. set_pass_through_walls_and_floor(false) elif is_jumping(): _velocity.y = -jump_strength _snap_vector = Vector2.ZERO # ...
Notice the last elif condition: we tackle pass-through in priority and only let the user jump if the previous conditions do not apply.
Here are our functions to check if the character is passing through walls and toggling passing through walls.
We use them to help you put a name on manipulating the collision layers.
## If set to `true`, makes the character pass through walls and the floor. func set_pass_through_walls_and_floor(value: bool) -> void: set_collision_mask_bit(PASS_THROUGH_LAYER, not value) ## Returns `true` if the character is currently in pass-through mode. func is_passing_through_walls() -> bool: return get_collision_mask_bit(PASS_THROUGH_LAYER) == false
There’s one last key function left: is_inside_wall().
When following through a platform, you need to decide when to turn collisions back on.
You don’t want to use only a timer or wait for the player to release the down key: both approaches will lead to edge cases where the player can get stuck in a floor or a wall if their collision shapes are big enough.
Instead, you want to use the physics engine and wait for the player to longer overlap the floor.
You could do that using an area 2D node. Here, I opted for doing a manual physics query via code. Those queries provide excellent performance and are a tool you want to know for games where you need many collision checks.
## Returns `true` if the character is inside the wall or the floor. func is_inside_wall() -> bool: # The query type allows us to make queries directly to the physics engine. var query := Physics2DShapeQueryParameters.new() # To make a query, we have to provide a collision shape, select the layer to # test for intersections, and the shape's transforms. query.set_shape(_collision_shape.shape) query.transform = _collision_shape.global_transform # Collision layers use bit flags: you can represent every combination of the # 20 physics layers by a unique integer. # # The left shift operator `<<` takes the binary value on the left, here a # single bit set to 1, and shifts it to the left several times, adding zeros # to the right as necessary. # # Here, it'll shift it 4 times, turning our value into the binary number # 10000. # # This allows us to set the bit corresponding to PASS_THROUGH_LAYER to 1. query.collision_layer = 1 << PASS_THROUGH_LAYER # We access the 2D physics server through the 2D world's # `Physics2DDirectSpaceState` and call its `intersect_shape()` method. # # The method returns an array of dictionaries, one for each shape we're # intersecting with. var intersecting_objects: Array = get_world_2d().direct_space_state.intersect_shape(query) return intersecting_objects.size() > 0
Let’s look at top-down character controllers.
In this example, you will learn to create a character that moves smoothly with acceleration and deceleration using the following steering behavior.
Moving in most top-down games is more straightforward than in platform games as we don’t have to think about gravity.
Like we did for Basic side-scrolling movement, we will look at a complete base top-down movement script here.
We start with some basic properties to keep track of the character’s velocity. Notice the drag property: we will use it to control the character’s inertia.
class_name BasePlayerTopDown extends KinematicBody2D ## The character's maximum speed in pixels per second. export var max_speed := 1000.0 ## A divider we use to limit the characters acceleration and deceleration. export var drag := 4.0 var _velocity := Vector2.ZERO
To move the character, we first calculate the input direction.
Here, we code straight inside of _physics_process().
func _physics_process(delta: float) -> void: # We calculate the move direction with support for analog movement using # `Input.get_action_strength()` # # It returns a value between 0.0 and 1.0 depending on how much you orient # the joystick in a given direction. var direction := Vector2.ZERO direction.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left") direction.y = Input.get_action_strength("move_down") - Input.get_action_strength("move_up") # Ensures the vector has a length of 1.0. If we don't do that, when pressing # both up and down, the direction vector has a length of 1 on both the X and # Y axes, thus a total length of about ~1.4. # # Without normalizing it, this would cause the character to move faster in # diagonal. direction = direction.normalized()
Next is the steering calculation.
Based on the player’s input direction, we calculate the velocity the character would have moved in that direction at the maximum speed.
This direction and maximum speed is the character’s “desired” velocity.
Steering consists of gradually going towards that desired velocity instead of applying it instantly. We achieve that with the following calculations.
func _physics_process(delta: float) -> void: # ... # We first calculate the desired or ideal velocity. var desired_velocity = direction * max_speed # The steering vector is the difference between the desired and current # velocities. var steering = desired_velocity - _velocity # We add part of the steering to the current velocity to get it closer to # `desired_velocity`. _velocity += steering / drag # And finally, we always ensure the player can't go past the maximum # `max_speed`. _velocity = _velocity.clamped(max_speed) # With that, we can move the character. # We can stick to the default properties for most of move_and_slide()'s arguments. _velocity = move_and_slide(_velocity)
Steering behaviors use vector calculations to smooth out a character’s motion, whether the player or the AI controls it. They are handy in many situations.
To learn more, check out our intro to steering behaviors in godot.
Lastly, you can register exceptions, other bodies on a layer that you want to ignore.
For example, in a game with a non-playable character that accompanies the hero, you might want to ignore collisions with that NPC for a short time during certain scenes or interactions.
When that is the case, you can use the add_collision_exception_with() function to add and then remove an exception.
# This is a fictional example var player: Player = $Player var npc: NPC = $NPC # The player won't collide with the NPC even if they're on the same physics layer. player.add_collision_exception_with(npc)
We saw how to interact with rigid bodies in another example: Pushing rigid bodies. Rigid bodies are ideal if you want the physics engine to help you, like when rolling a rock or bouncing a ball.
But what if you want to control how the player pushes and pulls something, like a crate in an old-school top-down game?
In that case, you’ll want to use another KinematicBody2D.
In this demo, on top of our top-down player, we have another KinematicBody2D scene: the rocks. We put them in a node group to detect them from code.
To push another body, like those rocks, what you want to do is to detect a collision with that body and “attach it” to your moving character.
In the code, you do that by storing a reference to the object to push.
You’ll then want to move that object before your character to avoid colliding with it.
# Reference to a rock to push. If set, every frame, we move the rock along with # the ship. # # You may experience stutters when trying to push multiple objects at once, as # we only track one at a time here. var _rock_to_push: KinematicBody2D = null ## Direction in which the player was moving when first pushing the rock. var _push_direction := Vector2.ZERO ## Threshold beyond which we detach a rock from the player. # In some cases, when you push the rock towards a surface and the character # splits away, we need this to stop the rock from moving away from the player. var _detach_distance := 0.0
We update the move direction every frame as in the Basic top-down movement with steering example.
func _physics_process(delta: float) -> void: var direction := Vector2.ZERO direction.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left") direction.y = Input.get_action_strength("move_down") - Input.get_action_strength("move_up") direction = direction.normalized()
If we have a rock to push, we move it before the ship, applying the ship’s velocity to it.
func _physics_process(delta: float) -> void: # ... # We need to move the rock before the player's ship to prevent stutters. # # We push the rock as long as the player keeps pushing in the same # direction. # # If they change direction, we remove the reference to the rock and stop # pushing. _velocity = direction * speed if _rock_to_push and direction == _push_direction: _rock_to_push.move_and_slide(_velocity) move_and_slide(_velocity)
We need to detect collisions with rocks and store a rock to push if need be.
func _physics_process(delta: float) -> void: # ... # Detecting and pushing rocks. # # We loop over everything we collided with this frame and check if it's a # rock. for index in get_slide_count(): var collision := get_slide_collision(index) var collider := collision.collider as KinematicBody2D # As we use the type hint `KinematicBody2D` above, if the `collider` is # not of that type, the variable will be `null`. if collider != null and collider.is_in_group("rock"): # If this is a rock to push, we store a reference to it and the # player's direction this frame. _rock_to_push = collider _push_direction = direction # If the rock is farther than this distance from the player, we stop # pushing it. We store the initial distance with a one-pixel margin. _detach_distance = global_position.distance_to(collider.global_position) + 1.0 break
Lastly, we need some code to stop pushing the rock if the player changes direction or gets pushed away from the rock by the environment:
if _rock_to_push: # There are two cases we want to stop pushing the rock: # # 1. The player changes direction. # 2. The rock and the player split over an angle in the environment, # causing the player and the rock to move away from one another. var rock_distance := global_position.distance_to(_rock_to_push.global_position) if direction != _push_direction or rock_distance > _detach_distance: _rock_to_push = null
You can apply the same techniques to code a pushing mechanic in 2D Zelda games. You would start a timer when initiating the push and only start pushing when the timer times out.
In this guide, we tried to keep the code short and relatively simple.
In your projects, you’ll likely want a clear separation for your characters’ different states.
For example, in the wall jump demo, we used coyote time to jump away from a wall.
Instead, you’d likely want the character to stick to the wall for a few frames upon moving away from the wall: just enough time for the player to perform a proper wall jump.
For this, Finite State Machines (FSM) are perfect.
They allow you to split your character’s code into a set of states, each of which has functions to initialize and update a given behavior, like sliding on a wall or wall jumping.
We created a complete guide to learn to make them in Godot: the Finite State Machine design pattern
KinematicBody2D is excellent for any physical object you want to move manually with fine control. This is the most commonly used node for characters in Godot.
Here is when you should use StaticBody2D, RigidBody2D, or Area2D instead:
Use a StaticBody2D for solid walls or ground, even for objects that affect the player’s movement, like conveyor belts.
Use a RigidBody2D for any physical object you want to move using physics properties like gravity and forces.
While you typically use them for physics games like Angry Birds, some studios also use rigid body physics for all their game characters.
Unlike the above, Area2D does not handle collisions with the environment. It only detects what enters and leaves it, whether it’s another area, a physics body, or the mouse cursor.
You can use it whenever you need to have something happen when entering or touching an entity, but you don’t need collisions. You’ll find many examples of that in the Area2D guide.
Godot used to only have move_and_collide() until version 2.0. The developers added the move_and_slide() function later to handle the most common ways people moved characters with a KinematicBody2D.
Often, move_and_slide() is all you need as it gives you several parameters to customize how the motion feels.
However, it is not meant to handle all the possible motions you would want for your games.
For one, you can use it to create mirroring lasers and other objects that bounce against walls. Although, if the shape of the projectile does not matter, you can use RayCast2D instead.
Then, it can be interesting to implement pixel-perfect motion yourself, using move_and_collide().
While you can perfectly prototype a low-res game with move_and_slide(), you might get better results with custom movement logic.
Another use case for move_and_collide() might be complex 360-degree movement like in Sonic games.
In any case, I would recommend trying to use move_and_slide() or move_and_slide_with_snap() and only switch to movement code when they show their limits. They are both easy to replace as they often are just one function call in your character script.
As with every other physics body or area, you want to use the physics layers and masks to control what your entity collides with.
Your body will collide with every physics layer set in its mask property.
If you want to react to a collision with a specific body, there are a few strategies you can use to check which body you collided with specifically:
We detailed those strategies and their pros and cons in the Area2D guide: Area2D.
Lastly, you can register exceptions, other bodies on a layer that you want to ignore.
For example, in a game with a non-playable character that accompanies the hero, you might want to ignore collisions with that NPC for a short time during certain scenes or interactions.
When that is the case, you can use the function to add and then remove an exception.
The KinematicBody2D class has a test_move function that we rarely use, but that can be very useful, especially when polishing your game.
You can think of it like a RayCast2D, but that projects your whole character’s collision shapes instead of just a line.
One use case would be to prevent a character from ending a dash inside a wall: by testing the motion in advance, regardless of the dash angle, we know the character will not get stuck.
In Unity tutorials, you’ll sometimes see people use many ray casts to make platform characters move and detect features of the environment.
In Godot, you can often use a single KinematicBody2D node and test_move() instead.
If the test motion would cause a collision, the function returns false. Otherwise, it returns true.